///////////////////////////////////////////////////////////////////////////////////
//
// Writer  : Dong-moon Lim, Dept. of Messenger, SK Communications, Korea
//
// Email   : dmlim@nate.com
//
// Created : 2008-06-05 11:31:40
//
// Description
//		This module's major function is encoding ASN.1 to DER format
//		CAPICOM uses "CAPICOM.EncryptedData" in Microsoft own format,
//		compatible with PKCS7 "Enveloped Data". 
//
// For more information 
//		http://msdn.microsoft.com/en-us/library/bb540802(VS.85).aspx
//
///////////////////////////////////////////////////////////////////////////////////

#if _MSC_VER > 1300 
#pragma warning( disable : 4996 )
#endif 

#ifdef WIN32
#include <windows.h>
#else
#include <netinet/in.h>
#include <string>
#include <algorithm>
#endif

#ifdef _WIN32_WCE
#include <winsock2.h>
#endif

#include "asn1.h"
#include "crypt_util.h"
#include "ncrypt_wchar.h"
#include "nmalloc.h"

#define EXCEPTION(m)						\
	{										\
		if ( e )							\
		{									\
			char line[100];					\
			sprintf(line, "%d", __LINE__);	\
			std::string str;				\
			str += __FILE__;				\
			str += "(";						\
			str += line;					\
			str += ") ";					\
			str += __FUNCTION__;			\
			str += ": ";					\
			str += (m);						\
			e->push_back(str);				\
		}									\
		return 0;							\
	}

#define TYPE_NAME_MAP(e,n) \
	gTypeNameMap.insert(std::pair<BYTE, const char*>(e, #n)); \
	if ( Object::isUniversal(e) && e > 0 ) gKnownUniversalTypeSet.insert(e);

namespace ASN1
{
	/////////////////////////////////////////////////////////////////////////////////////
	// class Object
	/////////////////////////////////////////////////////////////////////////////////////

	std::map<BYTE, const char*> Object::gTypeNameMap;
	std::set<BYTE> Object::gKnownUniversalTypeSet;

	int dummy = Object::Init();

	int Object::Init()
	{
		TYPE_NAME_MAP(UNIVERSAL, univ)

		TYPE_NAME_MAP(BOOLEAN, BOOLEAN)
		TYPE_NAME_MAP(INTEGER, INTEGER)
		TYPE_NAME_MAP(BIT_STRING, BIT STRING)
		TYPE_NAME_MAP(OCTET_STRING, OCTET STRING)
		TYPE_NAME_MAP(NULL_OBJECT, NULL)
		TYPE_NAME_MAP(OBJECT_ID, OBJECT)
		TYPE_NAME_MAP(UTF8_STRING, UTF8STRING)
		TYPE_NAME_MAP(PRNTBL_STRING, PRINTABLESTRING)
		TYPE_NAME_MAP(TELETEX_STRING, T61STRING)
		TYPE_NAME_MAP(IA5_STRING, IA5STRING)
		TYPE_NAME_MAP(UTCTIME, UTCTIME)
		TYPE_NAME_MAP(BMP_STRING, BMPSTRING)
		TYPE_NAME_MAP(SEQUENCE, SEQUENCE)
		TYPE_NAME_MAP(SET, SET)

		TYPE_NAME_MAP(APPLICATON, appl)

		TYPE_NAME_MAP(CONTEXT, cont)

		TYPE_NAME_MAP(PRIVATE, priv)

		return 0;
	}

	Object::Object(BYTE t)
	{
		m_type = t;

		std::map<BYTE, const char*>::iterator it = gTypeNameMap.find(t);
		m_name = it != gTypeNameMap.end()? it->second:0;

		if ( !m_name )
		{
			std::map<BYTE, const char*>::iterator it = gTypeNameMap.find(t & 0xc0);
			m_name = it != gTypeNameMap.end()? it->second:0;
		}

		m_offset = m_depth = m_head_len = m_body_len = 0;
		m_raw_byte = 0;
	}

	BYTE Object::GetType() 
	{ 
		return m_type; 
	}

	BYTE Object::GetTagNumber()
	{
		return m_type & 0x1f;
	}

	const char* Object::GetTypeName()
	{
		return m_name;
	}

	bool Object::isUniversal(BYTE type)
	{
		return (type & 0xc0) == 0x00;
	}

	bool Object::isApplication(BYTE type)
	{
		return (type & 0xc0) == 0x40;
	}

	bool Object::isContextDefined(BYTE type)
	{
		return (type & 0xc0) == 0x80;
	}

	bool Object::isPrivate(BYTE type)
	{
		return (type & 0xc0) == 0xc0;
	}

	bool Object::isPrimitive(BYTE type)
	{
		return (type & 0x20) == 0x00;
	}

	bool Object::isConstructed(BYTE type)
	{
		return (type & 0x20) == 0x20;
	}

	bool Object::isKnownUniversal(BYTE type)
	{
		return gKnownUniversalTypeSet.find(type) != gKnownUniversalTypeSet.end();
	}


	bool Object::isUniversal()
	{
		return isUniversal(m_type);
	}

	bool Object::isApplication()
	{
		return isApplication(m_type);
	}

	bool Object::isContextDefined()
	{
		return isContextDefined(m_type);
	}

	bool Object::isPrivate()
	{
		return isPrivate(m_type);
	}

	bool Object::isPrimitive()
	{
		return isPrimitive(m_type);
	}

	bool Object::isConstructed()
	{
		return isConstructed(m_type);
	}

	bool Object::isKnownUniversal()
	{
		return isKnownUniversal(m_type);
	}

	unsigned int Object::GetEncodedLength()
	{
		unsigned int cLen = GetContentLength();

		if ( cLen <= 127 )				return 1 + 1 + cLen;
		else if ( cLen & 0xff000000 )	return 1 + 1 + 4 + cLen;
		else if ( cLen & 0xffff0000 )	return 1 + 1 + 3 + cLen;
		else if ( cLen & 0xffffff00 )	return 1 + 1 + 2 + cLen;
		else							return 1 + 1 + 1 + cLen;
	}

	BYTE* Object::EncodeTypeAndLength(BYTE* buf)
	{
		unsigned int cLen = GetContentLength();
		unsigned int nLen = htonl(cLen);

		*buf++ = (BYTE)GetType();

		if ( cLen <= 127 )
		{
			*buf++ = (BYTE)cLen;
		}
		else if ( cLen & 0xff000000 )  
		{
			*buf++ = 0x84;
			CopyMemory(buf, (BYTE*)&nLen, 4);
			buf += 4;
		}
		else if ( cLen & 0xffff0000 )
		{
			*buf++ = 0x83;
			CopyMemory(buf, ((BYTE*)&nLen) + 1, 3);
			buf += 3;
		}
		else if ( cLen & 0xffffff00 )
		{
			*buf++ = 0x82;
			CopyMemory(buf, ((BYTE*)&nLen) + 2, 2);
			buf += 2;
		}
		else
		{
			*buf++ = 0x81;
			*buf++ = ((BYTE*)&nLen)[3];
		}

		return buf;
	}

	BYTE* Object::Encode(BYTE* buf)
	{
		buf = EncodeTypeAndLength(buf);
		buf = EncodeContent(buf);
		return buf;
	}

	Object* Object::Decode(BYTE* buf, /*in out*/ unsigned int *size, std::list<std::string> *e)
	{
		return Object::Decode(buf, 0, buf, size, e);
	}

	Object* Object::Decode(ASN_PARSE_DECL, BYTE* buf, /*in out*/ unsigned int *size, std::list<std::string> *e)
	{
		if ( !buf ) EXCEPTION("buf is null");
		if ( !size ) EXCEPTION("size is null");
		if ( *size < 2 ) EXCEPTION("size must be >= 2");

		BYTE* offset = buf;
		BYTE type = *offset++;
		BYTE len = *offset++;
		unsigned int cLen = 0;
		Object* obj = 0;
	
		if ( len & 0x80 )
		{
			BYTE lenByte = len & 0x7f;

			if ( lenByte == 0 ) EXCEPTION("length byte must be > 0");
			if ( buf + *size < offset + lenByte ) EXCEPTION("content length byte is larger than remain bytes");

			for ( ; !*offset && lenByte ; ++offset, --lenByte );

			if ( lenByte == 0 ) EXCEPTION("length byte must be > 0");
			if ( lenByte > 4 ) EXCEPTION("content length is too large (limit to 2^32-1)");

			for ( int i = 0 ; i < lenByte ; i++ )
				cLen |= (unsigned int)*offset++ << ((lenByte-i-1)*8);
		}
		else
		{
			cLen = len;
		}

		if ( buf + *size < offset + cLen ) EXCEPTION("content length is larger than remain bytes");

		switch ( type )
		{
		case BOOLEAN:			obj = new Boolean(); break;
		case INTEGER:			obj = new Integer(); break;
		case BIT_STRING:		obj = new BitString(); break;
		case OCTET_STRING:		obj = new OctetString(); break;
		case NULL_OBJECT:		obj = new NullObject(); break;
		case OBJECT_ID:			obj = new ObjectID(); break;
		case UTF8_STRING:		obj = new UTF8String(); break;
		case PRNTBL_STRING:		obj = new PrintableString(); break;
		case TELETEX_STRING:	obj = new TeletexString(); break;
		case IA5_STRING:		obj = new IA5String(); break;
		case UTCTIME:			obj = new UTCTime(); break;
		case BMP_STRING:		obj = new BMPString(); break;
		case SEQUENCE:			obj = new Sequence(); break;
		case SET:				obj = new Set(); break;
		default:				
			{
				if ( isConstructed(type) )
					obj = new Constructed(type);
				else
					obj = new UnknownObject(type); 
			}
		}

		obj->m_offset		= buf - asn_start;
		obj->m_depth		= asn_depth;
		obj->m_head_len		= offset - buf;
		obj->m_body_len		= cLen;
		obj->m_raw_byte		= offset;

		if ( cLen )
		{
			if ( !obj->DecodeContent(asn_start, asn_depth, offset, cLen, e) )
			{
				delete obj;
				obj = 0;
			}
		}

		offset += cLen;
		*size = (unsigned int)(offset - buf);

		return obj;
	}

	unsigned int GetDigit(unsigned int v)
	{
		unsigned int digits = 0;
		for ( ; (v /= 10) ; digits++);
		return digits+1;
	}

	void Object::Print(FILE* fp)
	{
		unsigned int offset = 0;
		unsigned int depth = 0;
		unsigned int head_len = 0;
		unsigned int body_len = 0;
		GetMaxLength(&offset, &depth, &head_len, &body_len);
		Print(GetDigit(offset), GetDigit(depth), GetDigit(head_len), GetDigit(body_len), fp);
	}

	void Object::Print(ASN_PRINT_DECL)
	{
		char format[100] = {0, };
		char name[50] = {0, };

		if ( isKnownUniversal() )
			sprintf(name, "%s", GetTypeName());
		else
			sprintf(name, "%s [ %x ]", GetTypeName(), GetTagNumber());

		sprintf(format, "%%%dd:d=%%%dd hl=%%%dd l=%%%dd %%s: %%%ds%%-16s", 
			w_offset, w_depth, w_head, w_body, m_depth);
		const char* category = isConstructed()? "cons":"prim";
		fprintf(fp, format, m_offset, m_depth, m_head_len, m_body_len, category, "", name);
		PrintContent(ASN_PRINT_CALL);
	}

	void Object::PrintRawBytes(ASN_PRINT_DECL)
	{
		if ( m_raw_byte && m_body_len )
		{
			int blocks = ((m_body_len-1) / 16) + 1;
			for ( int i = 0 ; i < blocks ; i++ )
			{
				char format[100] = {0, };
				char hex[49] = {0, };
				char bin[17] = {0, };
				sprintf(format, "%%%ds %%04x - ", w_offset);
				fprintf(fp, format, "", i*0x10);

				for ( int j = 0 ; j < 16 ; j++ )
				{
					unsigned int idx = i*16 + j;

					if ( idx < m_body_len )
					{
						BYTE ch = m_raw_byte[idx];
						sprintf(hex + j*3, "%02x%c", (int)ch, (j==7? '-':' '));
						bin[j] = (32 <= ch && ch <= 126)? ch:'.';
					}
					else
					{
						sprintf(hex + j*3, "   ");
						bin[j] = ' ';
					}
				}

				fprintf(fp, "%s %s\n", hex, bin);
			}
		}
	}

	void Object::GetMaxLength(unsigned int *offset, unsigned int *depth, unsigned int *head_len, unsigned int *body_len)
	{
#ifdef WIN32
		*offset	= max(*offset, m_offset);
		*depth = max(*depth, m_depth);
		*head_len = max(*head_len, m_head_len);
		*body_len = max(*body_len, m_body_len);
#else
		*offset	= std::max(*offset, m_offset);
		*depth = std::max(*depth, m_depth);
		*head_len = std::max(*head_len, m_head_len);
		*body_len = std::max(*body_len, m_body_len);
#endif
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class Boolean : Object
	/////////////////////////////////////////////////////////////////////////////////////

	Boolean::Boolean() : Object(BOOLEAN), m_value(false)
	{
	}

	Boolean::Boolean(bool v) : Object(BOOLEAN), m_value(v)
	{
	}

	Boolean::~Boolean()
	{
	}

	bool Boolean::GetValue()
	{
		return m_value;
	}

	void Boolean::SetValue(bool v)
	{
		m_value = v;
	}

	unsigned int Boolean::GetContentLength()
	{
		return 1;
	}

	BYTE* Boolean::EncodeContent(BYTE* buf)
	{
		*buf++ = m_value? 0xff:0x00;
		return buf;
	}

	BYTE* Boolean::DecodeContent(ASN_PARSE_DECL, BYTE* buf, unsigned int size, std::list<std::string> *e)
	{
		m_value = *buf++ ? true:false;
		return buf;
	}

	void Boolean::PrintContent(ASN_PRINT_DECL)
	{
		if ( m_raw_byte )
		{
			fprintf(fp, ":%02x (%s)\n", (int)*m_raw_byte, (m_value? "true":"false"));
		}
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class Integer : Object
	/////////////////////////////////////////////////////////////////////////////////////

	Integer::Integer() : Object(INTEGER), m_value(0) 
	{
	}

	Integer::Integer(int v) : Object(INTEGER), m_value(v) 
	{
	}

	Integer::~Integer()
	{
	}

	int Integer::GetValue() 
	{ 
		return m_value; 
	}

	void Integer::SetValue(int v) 
	{ 
		m_value = v; 
	}

	unsigned int Integer::GetContentLength()
	{
		if ( m_value == 0 )
		{
			return 1;
		}
		else if ( m_value & 0x80000000 )
		{
				 if ( !(m_value & 0xffffff80 ^ 0xffffff80) )	return 1;
			else if ( !(m_value & 0xffff8000 ^ 0xffff8000) )	return 2;
			else if ( !(m_value & 0xff800000 ^ 0xff800000) )	return 3;
			else												return 4;
		}
		else
		{
				 if ( !(m_value & 0x0000007f ^ m_value) )	return 1;
			else if ( !(m_value & 0x00007fff ^ m_value)	)	return 2;
			else if ( !(m_value & 0x007fffff ^ m_value) )	return 3;
			else								return 4;
		}
	}

	BYTE* Integer::EncodeContent(BYTE* buf)
	{
		unsigned int nVal = htonl((unsigned int)m_value);
		unsigned int cLen = GetContentLength();
		CopyMemory(buf, ((BYTE*)&nVal) + sizeof(m_value) - cLen, cLen);
		buf += cLen;

		return buf;
	}

	BYTE* Integer::DecodeContent(ASN_PARSE_DECL, BYTE* buf, unsigned int size, std::list<std::string> *e)
	{
		if ( size > sizeof(m_value) ) 
		{
			//EXCEPTION("integer value is too large (-2^31 ~ 2~31-1)");
		}
		else
		{
			m_value = (*buf & 0x80) ? -1:0;
			CopyMemory((BYTE*)&m_value + (sizeof(m_value)-size), buf, size);
			m_value = ntohl((unsigned int)m_value);
		}

		buf += size;
		return buf;
	}

	void Integer::PrintContent(ASN_PRINT_DECL)
	{
		if ( m_raw_byte )
		{
			fprintf(fp, ":");
			for ( unsigned int i = 0 ; i < m_body_len ; i++ )
			{
				fprintf(fp, "%02x", (int)m_raw_byte[i]);
			}
			fprintf(fp, " (%d)\n", m_value);
		}
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class NullObject : Object
	/////////////////////////////////////////////////////////////////////////////////////

	NullObject::NullObject() : Object(NULL_OBJECT)
	{
	}

	NullObject::~NullObject()
	{
	}

	unsigned int NullObject::GetContentLength()
	{
		return 0;
	}

	BYTE* NullObject::EncodeContent(BYTE* buf)
	{
		return buf;
	}

	BYTE* NullObject::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		return buf;
	}

	void NullObject::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, "\n");
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class BinaryObject : Object
	/////////////////////////////////////////////////////////////////////////////////////

	void BitString::Clear()
	{
		if ( m_data && m_iscopy ) free(m_data);
		m_data = 0;
		m_bit_size = 0;
		m_iscopy = false;
	}
	
	BitString::BitString() : Object(BIT_STRING), m_data(0), m_bit_size(0), m_iscopy(false)
	{
	}

	BitString::BitString(BYTE* data, unsigned int bit_size, bool iscopy)
		 : Object(BIT_STRING), m_data(0), m_bit_size(0), m_iscopy(false)
	{
		SetValue(data, bit_size, iscopy);
	}

	BitString::~BitString()
	{
		Clear();
	}

	void BitString::SetValue(BYTE* data, unsigned int bit_size, bool iscopy)
	{
		Clear();
		if ( bit_size )
		{
			m_bit_size = bit_size;

			if ( iscopy )
			{
				unsigned int byte_size = ((m_bit_size-1)>>3)+1;
				m_data = (BYTE*)malloc(byte_size);
				CopyMemory(m_data, data, byte_size);
			}
			else
			{
				m_data = data;
			}
		}
	}

	const BYTE* BitString::GetData()
	{
		return m_data;
	}

	unsigned int BitString::GetBitSize()
	{
		return m_bit_size;
	}

	unsigned int BitString::GetContentLength()
	{
		if ( m_bit_size )
		{
			return ((m_bit_size-1)>>3)+2;
		}
		else
		{
			return 0;
		}
	}

	BYTE* BitString::EncodeContent(BYTE* buf)
	{
		if ( m_bit_size )
		{
			unsigned int byte_size = ((m_bit_size-1)>>3)+1;
			unsigned int unused_bit = (8 - (m_bit_size%8)) % 8;
			*buf++ = (BYTE)unused_bit;
			CopyMemory(buf, m_data, byte_size);
			buf += byte_size;
		}

		return buf;
	}

	BYTE* BitString::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		BYTE* offset = buf;
		unsigned int unused_bit = *offset++;
		int remain_byte = buf + size - offset;
		if ( remain_byte > 0 )
		{
			unsigned int bit_size = remain_byte * 8 - unused_bit;
			SetValue(offset, bit_size);
			offset += remain_byte;
		}
		return offset;
	}

	void BitString::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, "\n");
		PrintRawBytes(ASN_PRINT_CALL);
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class BinaryObject : Object
	/////////////////////////////////////////////////////////////////////////////////////

	BinaryObject::BinaryObject(BYTE type) : Object(type), m_data(0), m_size(0), m_iscopy(false) 
	{
	} 

	BinaryObject::BinaryObject(BYTE type, BYTE* data, unsigned int size, bool iscopy) : 
		Object(type), m_data(0), m_size(0), m_iscopy(false)
	{
		SetValue(data, size, iscopy);
	}

	BinaryObject::~BinaryObject()
	{
		Clear();
	}

	void BinaryObject::Clear()
	{
		if ( m_data && m_iscopy ) free(m_data);
		m_data = 0;
		m_size = 0;
		m_iscopy = false;
	}

	void BinaryObject::SetValue(BYTE* data, unsigned int size, bool iscopy)
	{
		Clear();

		m_size = size;
		m_iscopy = iscopy;

		if ( size )
		{
			if ( iscopy )
			{
				m_data = (BYTE*)malloc(size);
				CopyMemory(m_data, data, size);
			}
			else
			{
				m_data = data;
			}
		}
	}

	const BYTE* BinaryObject::GetData()
	{
		return m_data;
	}

	unsigned int BinaryObject::GetSize()
	{
		return m_size;
	}

	unsigned int BinaryObject::GetContentLength()
	{
		return m_size;
	}

	BYTE* BinaryObject::EncodeContent(BYTE* buf)
	{
		if ( m_size )
		{
			CopyMemory(buf, m_data, m_size);
			buf += m_size;
		}
		return buf;
	}

	BYTE* BinaryObject::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		SetValue(buf, size);
		return buf + size;
	}

	void BinaryObject::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, "\n");
		PrintRawBytes(ASN_PRINT_CALL);
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class UnknownObject : BinaryObject : Object
	/////////////////////////////////////////////////////////////////////////////////////

	UnknownObject::UnknownObject(BYTE type) : BinaryObject(type)
	{
	}

	UnknownObject::UnknownObject(BYTE type, BYTE* data, unsigned int size, bool iscopy)
		: BinaryObject(type, data, size, iscopy)
	{
	}

	UnknownObject::~UnknownObject()
	{
	}

	void UnknownObject::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, "\n");
		PrintRawBytes(ASN_PRINT_CALL);
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class OctetString : BinaryObject : Object
	/////////////////////////////////////////////////////////////////////////////////////

	OctetString::OctetString() : BinaryObject(OCTET_STRING) 
	{
	}

	OctetString::OctetString(BYTE* data, unsigned int size, bool iscopy) 
		: BinaryObject(OCTET_STRING, data, size, iscopy) 
	{
	}

	OctetString::~OctetString()
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	String::String(BYTE type) : Object(type), m_unicode(0), m_utf8(0)
	{
	}

	String::String(BYTE type, const char* str) : Object(type), m_unicode(0), m_utf8(0)
	{
		m_utf8 = _strdup(str);
	}

	String::String(BYTE type, const WCHAR* str) : Object(type), m_unicode(0), m_utf8(0)
	{
		m_unicode = _wcsdup(str);
	}

	String::~String()
	{
		if ( m_utf8 ) free(m_utf8);
		if ( m_unicode ) free(m_unicode);
	}

	void String::Convert()
	{
		if ( m_utf8 )
		{
			m_unicode = UTF8toUTF16(m_utf8);
		}
		else if ( m_unicode )
		{
			m_utf8 = UTF16toUTF8(m_unicode);
		}
	}

	void String::SetString(const char* str, int len)
	{
		if ( m_utf8 ) { free(m_utf8); m_utf8 = 0; }
		if ( m_unicode ) { free(m_unicode); m_unicode = 0; }
		if ( len == -1 )
		{
			m_utf8 = _strdup(str);
		}
		else if ( len > 0 )
		{
			m_utf8 = (char*)malloc(len+1);
			CopyMemory(m_utf8, str, len);
			m_utf8[len] = 0;
		}
	}

	void String::SetString(const WCHAR* str, int len)
	{
		if ( m_utf8 ) { free(m_utf8); m_utf8 = 0; }
		if ( m_unicode ) { free(m_unicode); m_unicode = 0; }
		if ( len == -1 )
		{
			m_unicode = _wcsdup(str);
		}
		else if ( len > 0 )
		{
			m_unicode = (WCHAR*)malloc((len+1)*sizeof(WCHAR));
			CopyMemory(m_unicode, str, len*sizeof(WCHAR));
			m_unicode[len] = 0;
		}
	}

	const char* String::GetUTF8String()
	{
		if ( !m_utf8 && m_unicode )
			Convert();

		return m_utf8;
	}

	const WCHAR* String::GetUnicodeString()
	{
		if ( m_utf8 && !m_unicode )
			Convert();

		return m_unicode;
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class MultibyteString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	MultibyteString::MultibyteString(BYTE type) : String(type)
	{
	}

	MultibyteString::MultibyteString(BYTE type, const char* str) : String(type, str)
	{
	}

	MultibyteString::MultibyteString(BYTE type, const WCHAR* str) : String(type, str)
	{
	}

	MultibyteString::~MultibyteString()
	{
	}

	unsigned int MultibyteString::GetContentLength()
	{
		const char* str = GetUTF8String();
		int len = 0;
		if ( str ) len = strlen(str);
		return len;
	}

	BYTE* MultibyteString::EncodeContent(BYTE* buf)
	{
		const char* str = GetUTF8String();
		int len = 0;
		if ( str ) len = strlen(str);

		if ( len )
		{
			CopyMemory(buf, str, len);
			buf += len;
		}

		return buf;
	}

	BYTE* MultibyteString::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		SetString((const char*)buf, size);
		return buf + size;
	}

	void MultibyteString::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, ":%s\n", (m_utf8? m_utf8:""));
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class UTF8String : MultibyteString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	UTF8String::UTF8String() : MultibyteString(UTF8_STRING)
	{
	}

	UTF8String::UTF8String(const char* str) : MultibyteString(UTF8_STRING, str)
	{
	}

	UTF8String::UTF8String(const WCHAR* str) : MultibyteString(UTF8_STRING, str)
	{
	}

	UTF8String::~UTF8String()
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class IA5String : MultibyteString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	IA5String::IA5String() : MultibyteString(IA5_STRING)
	{
	}

	IA5String::IA5String(const char* str) : MultibyteString(IA5_STRING, str)
	{
	}

	IA5String::IA5String(const WCHAR* str) : MultibyteString(IA5_STRING, str)
	{
	}

	IA5String::~IA5String()
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class PrintableString : MultibyteString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	PrintableString::PrintableString() : MultibyteString(PRNTBL_STRING)
	{
	}

	PrintableString::PrintableString(const char* str) : MultibyteString(PRNTBL_STRING, str)
	{
	}

	PrintableString::PrintableString(const WCHAR* str) : MultibyteString(PRNTBL_STRING, str)
	{
	}

	PrintableString::~PrintableString()
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class TeletexString : MultibyteString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	TeletexString::TeletexString() : MultibyteString(TELETEX_STRING)
	{
	}

	TeletexString::TeletexString(const char* str) : MultibyteString(TELETEX_STRING, str)
	{
	}

	TeletexString::TeletexString(const WCHAR* str) : MultibyteString(TELETEX_STRING, str)
	{
	}

	TeletexString::~TeletexString()
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class UTCTime : MultibyteString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	UTCTime::UTCTime() : MultibyteString(UTCTIME)
	{
	}

	UTCTime::UTCTime(const char* str) : MultibyteString(UTCTIME, str)
	{
	}

	UTCTime::UTCTime(const WCHAR* str) : MultibyteString(UTCTIME, str)
	{
	}

	UTCTime::~UTCTime()
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class BMPString : String : Object
	/////////////////////////////////////////////////////////////////////////////////////

	BMPString::BMPString() : String(BMP_STRING)
	{
	}

	BMPString::BMPString(const char* str) : String(BMP_STRING, str)
	{
	}

	BMPString::BMPString(const WCHAR* str) : String(BMP_STRING, str)
	{
	}

	BMPString::~BMPString()
	{
	}

	unsigned int BMPString::GetContentLength()
	{
		const WCHAR* str = GetUnicodeString();
		int len = 0;
		if ( str ) len = _wcslen(str);
		return len * sizeof(WCHAR);
	}

	BYTE* BMPString::EncodeContent(BYTE* buf)
	{
		const WCHAR* str = GetUnicodeString();
		int len = 0;
		if ( str ) len = _wcslen(str);

		for ( int i = 0 ; i < len ; i++ )
		{
			*(WCHAR*)buf = htons(str[i]);
			buf += sizeof(WCHAR);
		}

		return buf;
	}

	BYTE* BMPString::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		if ( m_utf8 ) { free(m_utf8); m_utf8 = 0; }
		if ( m_unicode ) { free(m_unicode); m_unicode = 0; }

		unsigned int len = size / 2;
		m_unicode = (WCHAR*)malloc((len+1)*sizeof(WCHAR));
		for ( unsigned int i = 0 ; i < len ; i++ )
			m_unicode[i] = ntohs(((WCHAR*)buf)[i]);
		m_unicode[len] = 0;

		return buf + size;
	}

	void BMPString::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, "\n");
		PrintRawBytes(ASN_PRINT_CALL);
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class ObjectID : Object
	/////////////////////////////////////////////////////////////////////////////////////

	ObjectID::ObjectID() : Object(OBJECT_ID), m_oid(0) {}

	ObjectID::ObjectID(const char* oid) : Object(OBJECT_ID), m_oid(0)
	{
		SetValue(oid);
	}

	ObjectID::ObjectID(const WCHAR* oid) : Object(OBJECT_ID), m_oid(0)
	{
		SetValue(oid);
	}

	ObjectID::~ObjectID()
	{
		Clear();
	}

	void ObjectID::Clear()
	{
		if ( m_oid ) 
		{
			free(m_oid);
			m_oid = 0;
		}

		m_idList.clear();
	}

	void ObjectID::ParseID()
	{
		std::string tmp;

		for ( char* offset = m_oid ; *offset ; offset++ )
		{
			char ch = *offset;

			if ( '0' <= ch && ch <= '9' )
			{
				tmp += ch;
			}
			else if ( ch == '.' )
			{
				if ( tmp.length() > 0 )
				{
					m_idList.push_back(atoi(tmp.c_str()));
					tmp = "";
				}
			}
		}

		if ( tmp.length() > 0 )
		{
			m_idList.push_back(atoi(tmp.c_str()));
			tmp = "";
		}
	}

	void ObjectID::GenerateID()
	{
		std::string tmp;
		char buf[100];
		int size = m_idList.size();
		for ( int i = 0 ; i < size ; i++ )
		{
			sprintf(buf, (i ? ".%u":"%u"), m_idList[i]);
			tmp += buf;
		}
		m_oid = _strdup(tmp.c_str());
	}

	unsigned int ObjectID::GetFieldLength(unsigned int val)
	{
			 if ( val < 0x00000080 )	return 1;
		else if ( val < 0x00004000 )	return 2;
		else if ( val < 0x00200000 )	return 3;
		else if ( val < 0x10000000 )	return 4;
		else return 5;
	}

	BYTE* ObjectID::EncodeField(BYTE* buf, unsigned int val1, unsigned int val2)
	{
		*buf++ = (BYTE)(val1 * 40 + val2);
		return buf;
	}

	BYTE* ObjectID::DecodeField(BYTE* buf, unsigned int *val1, unsigned int *val2)
	{
		*val1 = *buf / 40;
		*val2 = *buf % 40;
		return ++buf;
	}

	BYTE* ObjectID::EncodeField(BYTE* buf, unsigned int val)
	{
		if ( val < 0x00000080 ) 
		{
			*buf++ = (BYTE)val;
		}
		else if ( val < 0x00004000 )
		{
			*buf++ = (BYTE)((val >> 7) | 0x80);
			*buf++ = (BYTE)(val & 0x7f);
		}
		else if ( val < 0x00200000 )
		{
			*buf++ = (BYTE)((val >> 14) | 0x80);
			*buf++ = (BYTE)((val >>  7) | 0x80);
			*buf++ = (BYTE)(val & 0x7f);
		}
		else if ( val < 0x10000000 )
		{
			*buf++ = (BYTE)((val >> 21) | 0x80);
			*buf++ = (BYTE)((val >> 14) | 0x80);
			*buf++ = (BYTE)((val >>  7) | 0x80);
			*buf++ = (BYTE)(val & 0x7f);
		}
		else
		{
			*buf++ = (BYTE)((val >> 28) | 0x80);
			*buf++ = (BYTE)((val >> 21) | 0x80);
			*buf++ = (BYTE)((val >> 14) | 0x80);
			*buf++ = (BYTE)((val >>  7) | 0x80);
			*buf++ = (BYTE)(val & 0x7f);
		}

		return buf;
	}

	BYTE* ObjectID::DecodeField(BYTE* buf, int len, unsigned int *val)
	{
		*val = 0;

		for ( int i = 0 ; i < len ; i++ )
		{
			*val |= (unsigned int)(*buf++ & 0x7f) << ((len-i-1)*7);
		}

		return buf;
	}

	void ObjectID::SetValue(const char* oid)
	{
		Clear();
		m_oid = _strdup(oid);
		ParseID();
	}

	void ObjectID::SetValue(const WCHAR* oid)
	{
		Clear();
		m_oid = UTF16toUTF8(oid);
		ParseID();
	}

	const char* ObjectID::GetValue()
	{
		return m_oid;
	}

	unsigned int ObjectID::GetContentLength()
	{
		int size = (int)m_idList.size();
		unsigned int cLen = 1;

		if ( size <= 2 ) 
		{
			return cLen;
		}
		else
		{
			for ( int i = 2 ; i < size ; i++ )
			{
				cLen += GetFieldLength(m_idList[i]);
			}
		}

		return cLen;
	}

	BYTE* ObjectID::EncodeContent(BYTE* buf)
	{
		int size = (int)m_idList.size();
		unsigned int first  = size > 0? m_idList[0]:0;
		unsigned int second = size > 1? m_idList[1]:0;
		buf = EncodeField(buf, first, second);

		for ( int i = 2 ; i < size ; i++ )
		{
			buf = EncodeField(buf, m_idList[i]);
		}

		return buf;
	}

	BYTE* ObjectID::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		unsigned int val1 = 0;
		unsigned int val2 = 0;
		BYTE* f_start = 0;

		BYTE* offset = DecodeField(buf, &val1, &val2);
		m_idList.push_back(val1);
		m_idList.push_back(val2);

		for ( f_start = offset ; buf + size > f_start ; f_start++ )
		{
			if ( !(*f_start & 0x80) )
			{
				int len = f_start - offset + 1;
				if ( len > 5 ) EXCEPTION("object id's field length is too large (must be <= 5)");
				offset = DecodeField(offset, len, &val1);
				m_idList.push_back(val1);
			}
		}

		GenerateID();

		return offset;
	}

	void ObjectID::PrintContent(ASN_PRINT_DECL)
	{
		fprintf(fp, ":%s\n", (m_oid? m_oid:""));
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class Constructed : Object
	/////////////////////////////////////////////////////////////////////////////////////

	Constructed::Constructed(BYTE type) : Object(type)
	{
	}

	Constructed::~Constructed()
	{
		ClearAll();
	}

	void Constructed::ClearAll()
	{
		unsigned int size = GetSize();

		if ( size )
		{
			for ( unsigned int i = 0 ; i < size ; i++ )
			{
				if ( m_objList[i].second )
				{
					Object* obj = m_objList[i].first;
					delete obj;
				}
			}

			m_objList.clear();
		}
	}

	unsigned int Constructed::GetSize()
	{
		return m_objList.size();
	}

	void Constructed::AddObject(Object* obj, bool destroy)
	{
		m_objList.push_back(std::pair<Object*, bool>(obj, destroy));
	}

	const Object* Constructed::GetObjectAt(unsigned int idx)
	{
		return m_objList.size() > idx ? m_objList[idx].first : 0;
	}

	unsigned int Constructed::GetContentLength()
	{
		unsigned int size = GetSize();
		unsigned int cLen = 0;

		for ( unsigned int i = 0 ; i < size ; i++ )
		{
			if ( m_objList[i].first )
			{
				cLen += m_objList[i].first->GetEncodedLength();
			}
		}

		return cLen;
	}

	BYTE* Constructed::EncodeContent(BYTE* buf)
	{
		unsigned int size = GetSize();

		for ( unsigned int i = 0 ; i < size ; i++ )
		{
			if ( m_objList[i].first )
			{
				buf = m_objList[i].first->Encode(buf);
			}
		}
		return buf;
	}

	BYTE* Constructed::DecodeContent(ASN_PARSE_DECL, BYTE *buf, unsigned int size, std::list<std::string> *e)
	{
		BYTE* offset = buf;

		while ( buf + size > offset )
		{
			unsigned int inout_size = buf + size - offset;
			Object *obj = Object::Decode(asn_start, asn_depth+1, offset, &inout_size, e);
			if ( obj )
			{
				AddObject(obj, true);
				offset += inout_size;
			}
			else
			{
				EXCEPTION("can't create Constructed object");
			}
		}

		return offset;
	}

	void Constructed::PrintContent(ASN_PRINT_DECL)
	{
		unsigned int size = GetSize();

		fprintf(fp, "\n");

		for ( unsigned int i = 0 ; i < size ; i++ )
		{
			if ( m_objList[i].first )
			{
				m_objList[i].first->Print(ASN_PRINT_CALL);
			}
		}
	}

	void Constructed::GetMaxLength(unsigned int *offset, unsigned int *depth, unsigned int *head_len, unsigned int *body_len)
	{
		Object::GetMaxLength(offset, depth, head_len, body_len);
		unsigned int size = GetSize();
		for ( unsigned int i = 0 ; i < size ; i++ )
		{
			if ( m_objList[i].first )
			{
				m_objList[i].first->GetMaxLength(offset, depth, head_len, body_len);
			}
		}
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class Sequence : Constructed : Object
	/////////////////////////////////////////////////////////////////////////////////////

	Sequence::Sequence() : Constructed(SEQUENCE) 
	{
	}

	Sequence::~Sequence() 
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class Set : Constructed : Object
	/////////////////////////////////////////////////////////////////////////////////////

	Set::Set() : Constructed(SET) 
	{
	}

	Set::~Set() 
	{
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class ContextDefinedObject : Constructed
	/////////////////////////////////////////////////////////////////////////////////////

	ContextDefinedObject::ContextDefinedObject(BYTE type, Object* obj, bool destroy)
		: Constructed(type)
	{
		AddObject(obj, destroy);
	}

	ContextDefinedObject::ContextDefinedObject(Object* obj, bool destroy)
		: Constructed(CONTEXT_CONST)
	{
		AddObject(obj, destroy);
	}

	ContextDefinedObject::~ContextDefinedObject()
	{
	}

	void ContextDefinedObject::SetValue(Object* obj, bool destroy)
	{
		ClearAll();
		AddObject(obj, destroy);
	}

	/////////////////////////////////////////////////////////////////////////////////////
	// class NamedObject : Sequence
	/////////////////////////////////////////////////////////////////////////////////////

	NamedObject::NamedObject() : Sequence()
	{
		m_oid = new ObjectID();
		m_cont = new ContextDefinedObject();
		SetList();
	}

	NamedObject::NamedObject(const char* oid, Object* obj) : Sequence()
	{
		m_oid = new ObjectID(oid);
		m_cont = new ContextDefinedObject(obj);
		SetList();
	}

	NamedObject::NamedObject(const WCHAR* oid, Object* obj) : Sequence()
	{
		m_oid = new ObjectID(oid);
		m_cont = new ContextDefinedObject(obj);
		SetList();
	}

	NamedObject::~NamedObject()
	{
		delete m_oid;
		delete m_cont;
	}

	void NamedObject::SetList()
	{
		AddObject(m_oid);
		AddObject(m_cont);
	}

	void NamedObject::SetValue(const char* oid, Object* obj)
	{
		m_oid->SetValue(oid);
		m_cont->SetValue(obj);
	}

	void NamedObject::SetValue(const WCHAR* oid, Object* obj)
	{
		m_oid->SetValue(oid);
		m_cont->SetValue(obj);
	}
}
